package com.naiterui.ehp.bs.b2c.order.service.impl;

import static com.naiterui.ehp.bs.b2c.order.entity.Order.PAY_STATUS_WAIT_PAY;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.google.common.collect.Lists;
import com.naiterui.common.repository.page.PageParamVO;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.agent.CustomOrderInfoBO;
import com.naiterui.ehp.bp.bo.agent.CustomOrderItemBO;
import com.naiterui.ehp.bp.bo.agent.CustomOrderReceiverBO;
import com.naiterui.ehp.bp.bo.b2c.OrderDeliveryBO;
import com.naiterui.ehp.bp.bo.b2c.ProductInventoryBO;
import com.naiterui.ehp.bp.bo.b2c.UpdateOrderBO;
import com.naiterui.ehp.bp.bo.b2c.param.InventoryParamBO;
import com.naiterui.ehp.bp.bo.patient.MsgMaPublishParamBO;
import com.naiterui.ehp.bp.bo.patient.PatientBO;
import com.naiterui.ehp.bp.bo.recommend.MedicationRecomDetailBO;
import com.naiterui.ehp.bp.bo.recommend.PrescriptionPlatformRecomInfoBO;
import com.naiterui.ehp.bp.bo.recommend.RecomOrderInfoBO;
import com.naiterui.ehp.bp.bo.recommend.param.RecomInfoParamBO;
import com.naiterui.ehp.bp.bo.statistics.AsOrderProductBO;
import com.naiterui.ehp.bp.bo.statistics.AsOrderRecordBO;
import com.naiterui.ehp.bp.constants.OrderSnType;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bp.utils.lang.StringUtil;
import com.naiterui.ehp.bp.utils.payment.OrderUtils;
import com.naiterui.ehp.bp.utils.payment.bo.CloseOrderResultBO;
import com.naiterui.ehp.bp.utils.payment.bo.NotifyBusinessPayBO;
import com.naiterui.ehp.bp.utils.payment.bo.UnifiedOrderRequestBO;
import com.naiterui.ehp.bp.utils.payment.bo.UnifiedOrderResultBO;
import com.naiterui.ehp.bp.utils.thread.ThreadPoolUtil;
import com.naiterui.ehp.bs.b2c.address.service.ICityService;
import com.naiterui.ehp.bs.b2c.address.service.IShippingInfoService;
import com.naiterui.ehp.bs.b2c.cart.service.ICartService;
import com.naiterui.ehp.bs.b2c.common.bo.address.CityBO;
import com.naiterui.ehp.bs.b2c.common.bo.address.ShippingInfoBO;
import com.naiterui.ehp.bs.b2c.common.bo.cart.CartItemBO;
import com.naiterui.ehp.bs.b2c.common.bo.inventory.StockQuantityBO;
import com.naiterui.ehp.bs.b2c.common.bo.order.PayTypeBO;
import com.naiterui.ehp.bs.b2c.common.remote.IB2cRemoter;
import com.naiterui.ehp.bs.b2c.common.util.UserUtils;
import com.naiterui.ehp.bs.b2c.constants.MallConstants;
import com.naiterui.ehp.bs.b2c.inventory.exception.StockServiceException;
import com.naiterui.ehp.bs.b2c.inventory.service.IStockService;
import com.naiterui.ehp.bs.b2c.order.constants.OrderConstants;
import com.naiterui.ehp.bs.b2c.order.constants.OrderStatusEnum;
import com.naiterui.ehp.bs.b2c.order.entity.LogisticsOrder;
import com.naiterui.ehp.bs.b2c.order.entity.LogisticsRouteOrder;
import com.naiterui.ehp.bs.b2c.order.entity.LogisticseCompany;
import com.naiterui.ehp.bs.b2c.order.entity.Order;
import com.naiterui.ehp.bs.b2c.order.entity.OrderItem;
import com.naiterui.ehp.bs.b2c.order.entity.OrderUser;
import com.naiterui.ehp.bs.b2c.order.entity.PayType;
import com.naiterui.ehp.bs.b2c.order.exception.OrderBusinessException;
import com.naiterui.ehp.bs.b2c.order.exception.OrderExceptionCodes;
import com.naiterui.ehp.bs.b2c.order.fegin.IOrderSearchFeignClient;
import com.naiterui.ehp.bs.b2c.order.fegin.IPatientFeignClient;
import com.naiterui.ehp.bs.b2c.order.fegin.IPharmacistFeignClient;
import com.naiterui.ehp.bs.b2c.order.fegin.IRecommendFeignClient;
import com.naiterui.ehp.bs.b2c.order.repository.ILogisticsOrderRepository;
import com.naiterui.ehp.bs.b2c.order.repository.ILogisticsRouteOrderRepository;
import com.naiterui.ehp.bs.b2c.order.repository.ILogisticseCompanyRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IOrderItemRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IOrderRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IOrderUserRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IPayTypeRepository;
import com.naiterui.ehp.bs.b2c.order.service.IOrderLogService;
import com.naiterui.ehp.bs.b2c.order.service.IOrderService;
import com.naiterui.ehp.bs.b2c.order.service.IRefundOrderService;
import com.naiterui.ehp.bs.b2c.order.vo.DeliveryTraceInfoVO;
import com.naiterui.ehp.bs.b2c.order.vo.OrderInfoInvoiceVO;
import com.naiterui.ehp.bs.b2c.order.vo.OrderInfoVO;
import com.naiterui.ehp.bs.b2c.order.vo.OrderItemVO;
import com.naiterui.ehp.bs.b2c.order.vo.OrderLiteVO;
import com.naiterui.ehp.bs.b2c.order.vo.OrderReceiverVO;
import com.naiterui.ehp.bs.b2c.order.vo.OrderStatisticalVO;
import com.naiterui.ehp.bs.b2c.order.vo.PlaceOrderParamVO;
import com.naiterui.ehp.bs.b2c.order.vo.PlaceOrderVO;
import com.naiterui.ehp.bs.b2c.order.vo.ShippingInfoVO;
import com.naiterui.ehp.bs.b2c.order.vo.UnifiedOrderResultVO;
import com.naiterui.ehp.bs.b2c.product.entity.Medicine;
import com.naiterui.ehp.bs.b2c.product.entity.Product;
import com.naiterui.ehp.bs.b2c.product.entity.Sku;
import com.naiterui.ehp.bs.b2c.product.repository.IMedicineRepository;
import com.naiterui.ehp.bs.b2c.product.repository.IProductRepository;
import com.naiterui.ehp.bs.b2c.product.repository.ISkuRepository;
import com.naiterui.ehp.bs.b2c.product.service.IInnerProductService;
import com.naiterui.ehp.bs.b2c.thirdparty.service.IPrescriptionPlatformService;
import com.naiterui.ehp.bs.b2c.thirdparty.vo.PlatformRecomProducVO;
import com.naiterui.ehp.bs.b2c.thirdparty.vo.PlatformRecomVO;
import com.naiterui.ehp.bs.b2c.thirdparty.vo.PrescriptionPlatformResponseVO;
import com.naiterui.ehp.bs.b2c.thirdparty.vo.ProductInventoryParamVO;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class OrderServiceImpl implements IOrderService {

    private static final String ORDER_AUTO_CANCEL_TIPS = "24小时内未付款，订单将自动取消";

    private final String b2cUrl = ConfigUtil.getString("b2c.url");

    @Autowired
    private ICartService cartService;
    @Autowired
    private ICityService cityService;
    @Autowired
    private IStockService stockService;
    @Autowired
    private IOrderLogService orderLogService;
    @Autowired
    private IOrderRepository orderRepository;
    @Autowired
    private IPayTypeRepository payTypeRepository;
    @Autowired
    private IPatientFeignClient patientFeignClient;
    @Autowired
    private IOrderItemRepository orderItemRepository;
    @Autowired
    private IOrderUserRepository orderUserRepository;
    @Autowired
    private IShippingInfoService shippingInfoService;
    @Autowired
    private IRecommendFeignClient recommendFeignClient;
    @Autowired
    private ILogisticsOrderRepository logisticsOrderRepository;
    @Autowired
    private ILogisticsRouteOrderRepository logisticsRouteOrderRepository;

    @Autowired
    private ILogisticseCompanyRepository logisticseCompanyRepository;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private ISkuRepository skuRepository;
    @Autowired
    private IProductRepository productRepository;
    @Autowired
    private IMedicineRepository medicineRepository;
    @Autowired
    private IB2cRemoter b2cRemoter;
    @Autowired
    private IPrescriptionPlatformService prescriptionPlatformService;
    @Autowired
    private IRefundOrderService refundOrderService;
    @Autowired
    private IInnerProductService innerProductService;
    @Autowired
    private IPharmacistFeignClient pharmacistFeignClient;
    @Autowired
    private IOrderSearchFeignClient searchFeignClient;

    @Override
    public List<PayTypeBO> getPayTypes() {
        List<PayTypeBO> payTypes = Lists.newArrayList();
        Iterable<PayType> dbPayTypes = this.payTypeRepository.findAll();
        for (PayType payType : dbPayTypes) {
            payTypes.add(new PayTypeBO(payType.getId(), payType.getName()));
        }
        return payTypes;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void cancel(Long orderId) throws BusinessException {
        Order order = this.orderRepository.get(orderId);
        //调用关单接口
        String orderSn = order.getOrderSn();
        this.closeOrder(orderSn);
        this.cancel(order, "订单超时未支付，系统主动取消订单", "系统");
    }

    /**
     * 关单
     */
    private void closeOrder(String orderSn) throws BusinessException {
        try {
            //调用关单接口
            CloseOrderResultBO orderResult = OrderUtils.close(orderSn);
            if (orderResult.getCloseOrderStatus() == CloseOrderResultBO.CLOSE_FAIL) {
                log.error("订单调用关单接口失败，orderSn:{} 失败原因:{}", orderSn, orderResult.getFailMsg());
                throw new OrderBusinessException(OrderExceptionCodes.ORDER_CANCEL_ERROR);
            }
        } catch (BusinessException e) {
            if (e.getCode() != BaseExceptionCodes.PAY_ORDER_NOT_FIND) {
                throw e;
            }
        }
        log.info("订单调用关单接口成功，orderSn:{} ", orderSn);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Long userId, Long orderId) throws BusinessException {
        Order order = this.orderRepository.findByIdAndUserId(orderId, userId);
        if (order == null) {
            log.warn("订单不存在 orderId:{}", orderId);
            throw new OrderBusinessException(OrderExceptionCodes.ORDER_NOT_FOUND);
        }
        // 仅未支付订单可取消
        Integer orderStatus = order.getOrderStatus();
        if (!OrderStatusEnum.PENDING.equals(OrderStatusEnum.getEnumByStatus(orderStatus))) {
            log.warn("仅未支付订单可取消 orderId:{}, orderStatus:{}", order.getId(), orderStatus);
            throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_CANNOT_CANCEL);
        }
        String orderSn = order.getOrderSn();
        //调用关单接口
        this.closeOrder(orderSn);

        this.cancel(order, "用户取消订单", null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Set<Long> orderIds, String operator) throws BusinessException {
        List<Order> orders = this.orderRepository.findByIdIn(orderIds);
        for (Order order : orders) {
            Long orderId = order.getId();
            Integer orderStatus = order.getOrderStatus();
            log.info("取消订单，orderId {}, orderStatus {}, payStatus {}", orderId, orderStatus, order.getPayStatus());
            if (orderStatus.equals(OrderStatusEnum.SHIPPING.getStatus()) || orderStatus
                .equals(OrderStatusEnum.RECEIVED.getStatus())
                || orderStatus.equals(OrderStatusEnum.CANCELLED.getStatus())) {
                log.info("已发货、已签收、已取消的订单不能再取消!!");
                // 已发货、已签收、已取消的订单不能再取消
                throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_ERROR);
            }

            // 如果没有预分配仓库，则不需要后续逻辑
            Long warehouseId = order.getWarehouseId();
            order.setWarehouseId(null);
            /**
             * 确认处方流转平台的状态为未发货:
             * 1、是的话就调用处方流转平台接口取消处方；
             * 2、否的话说明处方状态已刷新，变成已发货了，则提示不能取消(定时刷新状态接口过会会刷新订单状态)
             */
            // List<OrderItem> orderItems = orderItemRepository.findAllByOrderId(orderId);
            // Set<Long> recomIds = orderItems.stream().map(e -> e.getRecomId()).collect(Collectors.toSet());
            // // 确认处方流转平台的状态为未发货，实时维护isShipping变量值
            // Long recomId = recomIds.iterator().next();
            // PrescriptionPlatformResponseVO<PrescriptionRecomStatusVO> status = b2cRemoter
            // .getPrescriptionStatus(prescriptionPlatformService.getAccessToken(),
            // PlatformRecomStatusParamVO.builder().recipeIds(CollectionUtil.newArrayList(recomId.toString())).build());
            // List<PrescriptionRecomStatusVO> statusList = status.getData();
            // if (CollectionUtil.isNotEmpty(statusList)) {
            // PrescriptionRecomStatusVO recomStatusVO = statusList.get(0);
            // boolean isShipping = false;
            // List<String> canNotCancelStatus = CollectionUtil
            // .newArrayList(PrescriptionRecomStatusVO.ORDER_OUTBOUND, PrescriptionRecomStatusVO.ORDER_START_DELIVERY,
            // PrescriptionRecomStatusVO.ORDER_DELIVERYING, PrescriptionRecomStatusVO.ORDER_DELIVERY_FINISH,
            // PrescriptionRecomStatusVO.ORDER_RECEIVING);
            // if (recomStatusVO.getRecipeId().equals(recomId.toString()) &&
            // canNotCancelStatus.contains(recomStatusVO.getRxStatus())) {
            // log.info("处方已发货，不可取消订单，orderId {}, recomId {}", orderId, recomId);
            // // 已发货之后的状态
            // isShipping = true;
            // }
            //
            // if (!isShipping) {
            // log.info("取消订单，取消处方流转平台处方，recomId {}", recomId);
            // // 取消处方流转平台处方
            // PrescriptionPlatformResponseVO result = this.b2cRemoter
            // .calcelPlatformRecom(prescriptionPlatformService.getAccessToken(), recomIds.iterator().next());
            // } else {
            // // 提示订单已发货，不能取消
            // throw new BusinessException(OrderExceptionCodes.ORDER_DELIVERY_CANNOT_CALCEL);
            // }
            // }

            this.cancel(order, "CMS主动取消订单", operator);

            // if (orderStatus.equals(OrderStatusEnum.TRANSFER_SUCCESS.getStatus()) ||
            // orderStatus.equals(OrderStatusEnum.TRANSFER_FAIL.getStatus())) {
            // // 已支付的增加退款单
            // RefundOrderBO refundOrderBO = new RefundOrderBO();
            // log.info("取消订单，自动录入退款单！refundOrderBO {}", refundOrderBO);
            // refundOrderBO.setOrderId(orderId);
            // refundOrderBO.setMethod(1);
            // refundOrderBO.setReason(1);
            // refundOrderBO.setAmount(order.getTotalAmount());
            // refundOrderBO.setRemark("取消订单，系统自动建立退款单");
            // refundOrderService.update(refundOrderBO, 1, "system");
            // }

            // 库存相关业务逻辑无需再做处理
            /*if (warehouseId == null) {
                continue;
            }
            List<StockQuantityBO> stockQuantity = this.buildStockQuantity(orderId);
            if (orderStatus < OrderStatusEnum.ASSIGNED_COMPLETE.getStatus()) {
                //清空欲占
                this.stockService.cancel(orderId, order.getOrderSn(), warehouseId, 0, stockQuantity);
            } else if (orderStatus > OrderStatusEnum.AUDIT_COMPLETE.getStatus()) {
                //清空占用
                this.stockService.cancel(orderId, order.getOrderSn(), warehouseId, 1, stockQuantity);
            } else {
                log.warn("当前订单状态不做任何库存处理 orderId:{} orderStatus:{}", orderId, orderStatus);
            }*/
        }
    }

    private void cancel(Order order, String operatorContent, String operator) throws BusinessException {

        Integer beforeOrderStatus = order.getOrderStatus();

        if (OrderStatusEnum.PENDING.getStatus().equals(beforeOrderStatus)) {
            order.setCancelReason(Order.CANCEL_TYPE_NO_PAY);
        } else if (beforeOrderStatus > OrderStatusEnum.PENDING.getStatus() && beforeOrderStatus < OrderStatusEnum.RECEIVED.getStatus()) {
            order.setCancelReason(Order.CANCEL_TYPE_PAY);
        } else if (OrderStatusEnum.RECEIVED.getStatus().equals(beforeOrderStatus)) {
            order.setCancelReason(Order.CANCEL_TYPE_CONFIRM);
        }
        PatientBO patient = this.patientFeignClient.getBaseInfo(order.getUserId());
        String patientName = StringUtils.isEmpty(patient.getName()) ? patient.getNickName() : patient.getName();

        // 标记取消订单
        order.setOrderStatus(OrderStatusEnum.CANCELLED.getStatus());
        order.setChangedAt(new Date());
        String changedBy = StringUtils.isEmpty(operator) ? patientName : operator;
        order.setChangedBy(changedBy);
        this.orderRepository.update(order);
        Long orderId = order.getId();
        log.info("取消订单-订单标记取消 orderId:{}, orderSn:{}", orderId, order.getOrderSn());

        List<OrderItem> orderItems = this.orderItemRepository.findAllByOrderId(orderId);

        //通知推荐
        this.notificationRecommend(orderId, order.getOrderSn(), patientName, RecomOrderInfoBO.BUY_STATUS_CANCEL, orderItems);

        //增加日志
        this.orderLogService.save(orderId, operatorContent, beforeOrderStatus, OrderStatusEnum.CANCELLED.getStatus(), changedBy);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UnifiedOrderResultVO payOrder(Long orderId, String ip) throws BusinessException {
        Order order = this.orderRepository.get(orderId);
        if (order == null) {
            log.error("订单没有找到 orderId : {} IP : {}", orderId, ip);
            throw new OrderBusinessException(OrderExceptionCodes.ORDER_NOT_FOUND);
        }
        Integer orderStatus = order.getOrderStatus();
        if (!orderStatus.equals(OrderStatusEnum.PENDING.getStatus())) {
            throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_ERROR);
        }
        Long patientId = order.getUserId();
        PatientBO patient = this.patientFeignClient.getBaseInfo(patientId);
        String openId = patient.getMiniOpenId();
        String orderSn = order.getOrderSn();
        UnifiedOrderRequestBO unifiedOrderRequest = UnifiedOrderRequestBO.builder()
            // 订单号
            .orderSn(orderSn)
            // 商品名称
            .body(orderSn)
            // 用户OPEN id
            .openId(openId)
            // 用户ip
            .ip(ip)
            // 订单金额
            .totalFee(order.getRealPay())
            // 订单来源
            .origin(1)
            // 业务通知地址
            .notifyBusinessUrl(this.b2cUrl + "/inner/notify/pay")
            // 订单类型
            .orderType(OrderSnType.B2C_ORDERSN).build();
        UnifiedOrderResultBO unifiedOrderResult = OrderUtils.pay(unifiedOrderRequest);
        UnifiedOrderResultVO unifiedOrderResultVO = new UnifiedOrderResultVO();
        BeanUtils.copyProperties(unifiedOrderResult, unifiedOrderResultVO);
        return unifiedOrderResultVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PageVO<OrderLiteVO> list(PageParamVO pageParamVO, Integer status, Long userId) {
        // 查询订单主表信息
        Page<Order> orderPage;
        Pageable pageable = pageParamVO.getPageable(Sort.by(Sort.Order.desc("createdAt")));
        if (status == null || status == 0) {
            orderPage = this.orderRepository.findAllByUserId(userId, pageable);
        } else {
            Set<Integer> orderStatus = OrderStatusEnum.getStatusByFrontDeskStatus(status);
            orderPage = this.orderRepository.findByUserIdAndOrderStatusIn(userId, orderStatus, pageable);
        }
        if (orderPage.getTotalElements() == 0) {
            return PageVO.newBuilder()
                .pageNo(pageParamVO.getPage()).pageSize(pageParamVO.getNum())
                .totalCount((int)orderPage.getTotalElements()).result(Lists.newArrayList()).build();
        }
        // 查询订单商品明细
        List<Order> orderList = orderPage.getContent();
        List<Long> orderIdList = orderList.stream().map(Order::getId).collect(Collectors.toList());
        List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderIdIn(orderIdList);

        Map<Long, List<OrderItem>> itemMap = orderItemList.stream().collect(Collectors.groupingBy(OrderItem::getOrderId));

        Function<Order, OrderLiteVO> orderOrderLiteFunction = order -> {
            OrderStatusEnum orderStatusEnum = OrderStatusEnum.getFrontDeskStatus(order.getOrderStatus());
            List<OrderItem> itemList = itemMap.get(order.getId());
            return OrderLiteVO.builder()
                .orderId(order.getId()).orderSn(order.getOrderSn()).quantity(this.getItemCount(itemList))
                .realPay(order.getRealPay()).status(order.getOrderStatus())
                .statusDescribe(orderStatusEnum.getStatusDescribe())
                // 待付款状态提示剩余时间
                .tips(OrderStatusEnum.PENDING.equals(orderStatusEnum) ? ORDER_AUTO_CANCEL_TIPS : "")
                .items(this.transferOrderItem(itemList)).freight(0).build();
        };

        List<OrderLiteVO> orderLiteList = orderList.stream().map(orderOrderLiteFunction).collect(Collectors.toList());
        return PageVO.newBuilder()
            .pageNo(pageParamVO.getPage()).pageSize(pageParamVO.getNum()).totalCount((int)orderPage.getTotalElements())
            .result(orderLiteList).build();
    }

    /**
     * 商品总数计算
     */
    private Integer getItemCount(List<OrderItem> itemList) {
        return Optional.ofNullable(itemList).orElse(Lists.newArrayList()).stream().mapToInt(OrderItem::getQuantity).sum();
    }

    /**
     * 商品明细VO转换
     */
    private List<OrderItemVO> transferOrderItem(List<OrderItem> orderItemList) {
        Function<OrderItem, OrderItemVO> orderItemOrderItemFunction = orderItem -> OrderItemVO.builder()
            .productId(orderItem.getProductId()).skuId(orderItem.getSkuId()).name(orderItem.getSkuName())
            .rx(orderItem.getRx()).quantity(orderItem.getQuantity()).price(orderItem.getSalePrice())
            .icon(orderItem.getIcon()).build();
        return Optional.ofNullable(orderItemList)
            .orElse(Lists.newArrayList()).stream().map(orderItemOrderItemFunction).collect(Collectors.toList());
    }

    /**
     * 商品明细VO转换
     */
    private List<CustomOrderItemBO> transferCustomOrderItemBO(List<OrderItem> orderItemList) {
        Function<OrderItem, CustomOrderItemBO> orderItemOrderItemFunction =
            orderItem -> CustomOrderItemBO.builder().productId(orderItem.getProductId()).skuId(orderItem.getSkuId())
                .name(orderItem.getSkuName()).rx(orderItem.getRx()).quantity(orderItem.getQuantity())
                .price(orderItem.getSalePrice()).icon(orderItem.getIcon()).build();
        return Optional.ofNullable(orderItemList).orElse(Lists.newArrayList()).stream().map(orderItemOrderItemFunction)
            .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderInfoVO getOrderDetail(Long orderId, Long userId) throws BusinessException {
        Order order = this.orderRepository.findByIdAndUserId(orderId, userId);
        if (order == null) {
            log.warn("订单不存在userId:{}, order:{}", userId, orderId);
            throw new BusinessException(OrderExceptionCodes.ORDER_NOT_FOUND);
        }

        List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderId(orderId);
        OrderUser orderUser = this.orderUserRepository.findByOrderIdAndType(orderId, OrderUser.ORDER_TYPE_SHOPPING);

        int itemQuantity = orderItemList.stream().mapToInt(OrderItem::getQuantity).sum();

        String carrier = "";
        LogisticsOrder logisticsOrder = this.logisticsOrderRepository.findByOrderId(orderId);
        if (logisticsOrder != null && null != logisticsOrder.getLogisticsCompanyId()) {
            LogisticseCompany logisticseCompany = this.logisticseCompanyRepository.get(logisticsOrder.getLogisticsCompanyId());
            carrier = logisticseCompany.getName();
        }

        OrderStatusEnum orderStatusEnum = OrderStatusEnum.getFrontDeskStatus(order.getOrderStatus());
        return OrderInfoVO.builder()
            .orderId(order.getId()).orderSn(order.getOrderSn()).itemQuantity(itemQuantity)
            .orderTime(order.getCreatedAt()).totalAmount(order.getTotalAmount()).freight(order.getFreight())
            .couponPay(order.getCouponPay()).realPay(order.getRealPay()).status(order.getOrderStatus())
            .statusName(orderStatusEnum.getFrontDeskStatusDescribe())
            // 待付款状态提示剩余时间
            .tips(OrderStatusEnum.PENDING.equals(orderStatusEnum) ? ORDER_AUTO_CANCEL_TIPS : "")
            .items(this.transferOrderItem(orderItemList)).remark(order.getRemark())
            .invoice(order.getNeedInvoice() == Order.INVOICE_NO_NEED ? null
                : OrderInfoInvoiceVO.builder().title(order.getInvoiceTitle()).type(OrderConstants.INVOICE_TYPE_PAPER)
                    .content(order.getInvoiceContent()).taxNo(order.getInvoiceTaxNo())
                    // TODO 对接电子发票后调整
                    .url("http://www.baidu.com").build())
            .receiverInfo(OrderReceiverVO.builder().receiver(orderUser.getReceiver()).phone(orderUser.getPhone())
                .province(orderUser.getProvice()).city(orderUser.getCity()).county(orderUser.getCountry())
                .addr(orderUser.getAddress()).build())
            .payType("微信支付")
            // 查询物流
            .carrier(carrier).build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PlaceOrderVO createOrder(PlaceOrderParamVO placeOrderParamVO) throws BusinessException {
        //保存订单。。
        Long userId = UserUtils.getUser().getPatientId();
        Order order = new Order();

        //订单状态 待支付
        order.setOrderStatus(OrderStatusEnum.PENDING.getStatus());
        //支付状态 待支付
        order.setPayStatus(PAY_STATUS_WAIT_PAY);
        order.setPayType(placeOrderParamVO.getPayType());

        List<CartItemBO> cartItems = this.cartService.getCartItems(userId);
        // 库存判断
        this.checkInventory(cartItems);

        List<ProductInventoryParamVO> drugList = new ArrayList<>();
//        b2cRemoter.getProductInventory()
        int totalAmount = cartItems.stream().mapToInt(value -> value.getSalePrice() * value.getQuantity()).sum();

        String orderSn = OrderUtils.getOrderSn(OrderSnType.B2C_ORDERSN);
        order.setOrderSn(orderSn);

        //商品总价
        order.setTotalAmount(totalAmount);
        // 判断价格是否和用户看到的一样
        if (totalAmount != placeOrderParamVO.getTotalAmount()) {
            throw new OrderBusinessException(OrderExceptionCodes.TOTAL_AMOUNT_ERROR);
        }

        // 患者认证状态校验
        /*Integer status = this.cartService.getPatientRealNameStatus(userId);
        if (CartConstants.SETTLEMENT_USER_REAL_NAME_STATUS_PASS != status) {
            log.warn("下单失败，用户未通过实名认证, userId:{}", userId);
            throw new BusinessException(OrderExceptionCodes.USER_REAL_NAME_STATUS_UNPASS);
        }*/

        int couponPay = 0;

        order.setCouponPay(couponPay);
        //邮费(分)
        order.setFreight(MallConstants.FREIGHT);
        //实付
        int realPay = totalAmount + MallConstants.FREIGHT - couponPay;
        // 应付金额小于0的校验
        if (realPay <= 0) {
            //订单状态 待发货
            order.setOrderStatus(OrderStatusEnum.PENDING.getStatus());
            //支付状态 已支付
            order.setPayStatus(Order.PAY_STATUS_PAY);
            realPay = 0;
        }
        //取消原因
        order.setCancelReason(Order.CANCEL_TYPE_NORMAL);
        order.setRealPay(realPay);
        order.setNeedInvoice(Order.INVOICE_NO_NEED);
        /*//是否需要发票(0:不需要,1:需要)
        InvoiceVO invoiceVo = placeOrderParamVO.getInvoice();
        Integer isInvoice = invoiceVo.getIsInvoice();
        order.setNeedInvoice(isInvoice);
        if (isInvoice == InvoiceVO.INVOICE_NEED) {
            log.debug("用户填写发票信息 userId:{} {}", userId, invoiceVo);
            //发票抬头
            order.setInvoiceTitle(invoiceVo.getTitle());
            order.setInvoiceContent("药品");
            //发票邮箱
            order.setInvoiceEmail(invoiceVo.getEmail());
            //发票电话
            order.setInvoicePhone(invoiceVo.getPhone());
            String taxNo;
            if (invoiceVo.getTitleType() == 0) {
                taxNo = "";
            } else {
                taxNo = invoiceVo.getTaxNo();
                if (StringUtils.isEmpty(taxNo) || StringUtils.isEmpty(invoiceVo.getTitle())) {
                    log.warn("发票税号与发票title不能为空");
                    throw new OrderBusinessException(OrderExceptionCodes.PARAM_ERROR);
                }
            }
            //发票税号
            order.setInvoiceTaxNo(taxNo);
        }*/

        //订单备注
        order.setRemark(placeOrderParamVO.getRemark());

        PatientBO patient = this.patientFeignClient.getBaseInfo(userId);
        String patientName = patient.getName();

        order.setUserId(userId);
        order.setCreatedAt(new Date());
        order.setCreatedBy(patientName);
        order.setChangedAt(new Date());
        order.setChangedBy(patientName);
        this.orderRepository.save(order);
        //保存订单明细
        Long orderId = order.getId();
        List<OrderItem> orderItems = this.buildOrderItems(orderId, cartItems, patientName);
        //保存订单用户表
        this.buildOrderUser(userId, orderId, placeOrderParamVO.getShippingInfoId(), patientName);

        //清空购物车
        List<Long> itemsIds = cartItems.stream().map(CartItemBO::getItemId).collect(Collectors.toList());
        this.cartService.clearCart(userId, itemsIds);

        this.notificationRecommend(orderId, orderSn, patientName, RecomOrderInfoBO.BUY_STATUS_WATI_PAY, orderItems);

        //增加日志
        this.orderLogService.save(orderId, "用户下单,等待用户支付。", null, OrderStatusEnum.PENDING.getStatus(), patientName);

        //保存订单物流记录信息
        // LogisticsOrder logisticsOrder = LogisticsOrder.builder()
        // .orderId(orderId)
        // .orderSn(order.getOrderSn())
        // .type(LogisticsOrder.TYPE_NORMAL_DISTRIBUTION_LIST)
        // .status(LogisticsOrder.STATUS_NORMAL)
        // .createdAt(new Date())
        // .createdBy("system")
        // .changedAt(new Date())
        // .changedBy("system")
        // .build();
        // logisticsOrderRepository.save(logisticsOrder);
        return new PlaceOrderVO(orderId, totalAmount, realPay);

    }

    private void checkInventory(List<CartItemBO> cartItems) throws BusinessException {
        // 判断库存以及库存预警值
        List<InventoryParamBO> goodsList = cartItems.stream().map(e -> InventoryParamBO.builder().num(e.getQuantity()).skuId(e.getSkuId()).build())
            .collect(Collectors.toList());
        List<ProductInventoryBO> inventoryBOS = innerProductService.getInventoryInfoList(goodsList);
        StringBuffer sb = new StringBuffer();
        List<Long> shortedSkuIds = new ArrayList<>();
        inventoryBOS.stream().forEach(e -> {
            // 缺货 或 库存预警 或者下架
            if (e.getShorted().equals(ProductInventoryBO.SHORTED_YES)
                || e.getInventoryWarning().equals(ProductInventoryBO.INVENTORY_WARNING_YES) || !e.getSale()) {
                shortedSkuIds.add(e.getSkuId());
            }
        });
        String str = "";
        if (CollectionUtil.isNotEmpty(shortedSkuIds)) {
            List<Sku> skuList = skuRepository.findAllByIdIn(shortedSkuIds);
            str = skuList.stream().map(e -> e.getName()).collect(Collectors.joining(","));
            sb.append("您所购买的药品").append(str).append("库存不足或者已下架，请稍后重试或联系医生修改处方重新购买");
            throw new BusinessException(OrderExceptionCodes.PRODUCT_INVENTORY_NOT_FULL, sb.toString());
        }
    }

    private void notificationRecommend(Long orderId, String orderSn, String patientName, Integer buyStatus, List<OrderItem> orderItems) {
        log.info("下单通知调用推荐 请求参数：orderId = {}, orderSn = {}, patientName = {}, buyStatus = {},", orderId, orderSn, patientName, buyStatus);

        RecomOrderInfoBO recomOrderInfo = new RecomOrderInfoBO();
        recomOrderInfo.setOrderId(orderId);
        recomOrderInfo.setBuyStatus(buyStatus);
        recomOrderInfo.setOrderSN(orderSn);
        List<RecomInfoParamBO> recomInfo = orderItems.stream()
            .filter(cartItem -> cartItem.getRecomId() != 0)
            .map(cartRecom -> RecomInfoParamBO.builder().doctorId(cartRecom.getDoctorId())
                .recommendId(cartRecom.getRecomId()).type(RecomInfoParamBO.TEMPLATE_TYPE_RECOMMEND_ALLBUY)
                .patientName(patientName).isPublic(1).build())
            .collect(Collectors.toList());
        recomOrderInfo.setRecomInfo(recomInfo);
        try {
            this.recommendFeignClient.updateOrderRecom(recomOrderInfo);
        } catch (BusinessException e) {
            log.error("下单通知推荐失败 请求参数：orderId = {}, orderSn = {}, patientName = {}, buyStatus = {},", orderId, orderSn, patientName, buyStatus, e);
        }
    }

    /**
     * 构建订单详情
     */
    private List<OrderItem> buildOrderItems(Long orderId, List<CartItemBO> cartItems, String patientName) {
        List<OrderItem> orderItems = cartItems.stream().map(cartItem -> OrderItem.builder()
            .orderId(orderId).doctorId(cartItem.getDoctorId()).recomId(cartItem.getRecomId())
            .productId(cartItem.getProductId()).productName(cartItem.getProductName()).skuId(cartItem.getSkuId())
            .skuName(cartItem.getFullName()).icon(cartItem.getIcon()).quantity(cartItem.getQuantity())
            .salePrice(cartItem.getSalePrice()).rx(cartItem.getRx()).createdAt(new Date()).createdBy(patientName)
            .changedAt(new Date()).changedBy(patientName).build()).collect(Collectors.toList());
        this.orderItemRepository.saveAll(orderItems);
        return orderItems;
    }

    /**
     * 构建订单用户表
     *
     * @param orderId 订单ID
     */
    private void buildOrderUser(Long userId, Long orderId, Long shippingInfoId, String patientName) throws OrderBusinessException {
        Assert.notNull(orderId, "订单ID不能为null");
        Assert.notNull(shippingInfoId, "收货地址ID不能为null");

        ShippingInfoBO shippingInfo = this.shippingInfoService.getShippingInfoById(shippingInfoId, userId);
        if (shippingInfo == null) {
            throw new OrderBusinessException(OrderExceptionCodes.CART_NOT_PRODUCT);
        }
        //保存收货信息
        this.orderUserRepository.save(OrderUser.builder()
            .type(OrderUser.ORDER_TYPE_SHOPPING).orderId(orderId).receiver(shippingInfo.getReceiver())
            .provice(shippingInfo.getProvince()).city(shippingInfo.getCity()).country(shippingInfo.getCounty())
            .cityId(shippingInfo.getCityId()).address(shippingInfo.getAddr()).zipcode(shippingInfo.getZipcode())
            .phone(shippingInfo.getPhone()).createdAt(new Date()).createdBy(patientName).changedAt(new Date())
            .changedBy(patientName).build());
    }

    @Override
    @Transactional(propagation = Propagation.NEVER)
    public void notifyOrderPayStatus(NotifyBusinessPayBO notifyBusinessPay) throws OrderBusinessException {
        String orderSn = notifyBusinessPay.getOrderSn();
        if (notifyBusinessPay.getPayStatus() != NotifyBusinessPayBO.PAY_SUCCESS) {
            log.warn("订单支付失败,orderSn : {} notifyBusinessPayBO : {}", orderSn, notifyBusinessPay);
            throw new OrderBusinessException(OrderExceptionCodes.ILLEGAL_REQUEST);
        }
        Order order = this.orderRepository.findByOrderSn(orderSn);
        if (order == null) {
            log.error("订单没有找到 orderSn : {}", orderSn);
            throw new OrderBusinessException(OrderExceptionCodes.ORDER_NOT_FOUND);
        }
        if (order.getPayStatus() == Order.PAY_STATUS_PAY) {
            log.warn("订单已支付 orderSn:{}", orderSn);
            return;
        }
        if (!order.getOrderStatus().equals(OrderStatusEnum.PENDING.getStatus())) {
            log.warn("订单不是待支付，不能处理后续逻辑.orderSn:{}", orderSn);
            throw new OrderBusinessException(OrderExceptionCodes.ILLEGAL_REQUEST);
        }
        Long orderId = order.getId();
        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                //增加日志
                OrderServiceImpl.this.orderLogService
                    .save(orderId, "用户支付成功，处方流转成功。", OrderStatusEnum.PENDING.getStatus(),
                        OrderStatusEnum.TRANSFER_SUCCESS.getStatus(), "微信支付");
                order.setOrderStatus(OrderStatusEnum.WAIT_AUDIT.getStatus());
                order.setPayStatus(Order.PAY_STATUS_PAY);
                order.setChangedAt(new Date());
                order.setChangedBy("微信支付");
                OrderServiceImpl.this.orderRepository.update(order);

                List<OrderItem> items = OrderServiceImpl.this.orderItemRepository.findAllByOrderId(orderId);
                // 通知处方服务
                OrderServiceImpl.this.notificationRecommend(orderId, orderSn, "", RecomOrderInfoBO.BUY_STATUS_ALL, items);
            }
        });

        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    List<StockQuantityBO> pqs = OrderServiceImpl.this.buildStockQuantity(orderId);
                    Long warehouseId = OrderServiceImpl.this.stockService.preAssignWarehouse(orderId, order.getOrderSn(), pqs);
                    log.info("订单预分配仓库成功 orderId:{} warehouseId:{}", orderId, warehouseId);
                    Order order = OrderServiceImpl.this.orderRepository.findByOrderSn(orderSn);
                    order.setWarehouseId(warehouseId);
                    Integer orderStatus = order.getOrderStatus();
                    //增加日志
                    if (orderStatus.equals(OrderStatusEnum.WAIT_AUDIT.getStatus())) {
                        OrderServiceImpl.this.orderLogService.save(orderId, "预分配仓库完成，等待人工/系统审核。", orderStatus, OrderStatusEnum.WAIT_AUDIT.getStatus(), "微信支付");
                    } else {
                        OrderServiceImpl.this.orderLogService.save(orderId, "预分配仓库完成，等待库房分配。", orderStatus, OrderStatusEnum.AUDIT_COMPLETE.getStatus(), "微信支付");
                    }
                    order.setChangedAt(new Date());
                    order.setChangedBy("微信支付");
                    order.setOrderStatus(OrderStatusEnum.WAIT_AUDIT.getStatus());
                    OrderServiceImpl.this.orderRepository.update(order);
                } catch (StockServiceException e) {
                    transactionStatus.setRollbackOnly();
                    log.error("系统预分配仓库失败 orderId:{} message:{}", order.getId(), e.getMessage());
                } catch (Exception e) {
                    transactionStatus.setRollbackOnly();
                    log.error("系统预分配仓库失败 orderId:{}", order.getId(), e);
                }

                // 异步同步订单数据到统计
                ThreadPoolUtil.execute(() -> {
                    List<OrderItem> items = OrderServiceImpl.this.orderItemRepository.findAllByOrderId(orderId);
                    searchFeignClient.createOrderRecordIndex(
                        AsOrderRecordBO.builder().doctorId(items.get(0).getDoctorId()).patientId(order.getUserId())
                            .recipelId(items.get(0).getRecomId()).orderProductList(items.stream().map(i -> {
                                AsOrderProductBO orderProductBO = new AsOrderProductBO();
                                orderProductBO.setProductId(i.getProductId());
                                orderProductBO.setProductNumber(i.getQuantity());
                                orderProductBO.setProductPrice(i.getSalePrice() * i.getQuantity());
                                return orderProductBO;
                            }).collect(Collectors.toList()))
                            .productNumber(items.stream().mapToInt(OrderItem::getQuantity).sum())
                        .orderSn(orderSn).orderPrice(order.getRealPay()).orderTime(order.getCreatedAt())
                        .generateDate(new Date()).createdAt(new Date()).createdBy("system").build());
                });
                // 异步给处方流转平台同步处方，通知其进行发货
                /*ThreadPoolUtil.execute(() -> {
                    PrescriptionPlatformResponseVO result = synchroPrescriptionPlatformRecom(orderId);
                    if (!result.isSuccess()) {
                        // 补发机制
                        result = synchroPrescriptionPlatformRecom(orderId);
                    }
                    Order synchroOrder = orderRepository.get(orderId);
                    if (!result.isSuccess()) {
                        // 同步失败更正订单状态
                        synchroOrder.setOrderStatus(OrderStatusEnum.TRANSFER_FAIL.getStatus());
                        orderRepository.update(synchroOrder);
                        //增加日志
                        OrderServiceImpl.this.orderLogService
                            .save(orderId, "用户支付成功，处方流转失败。", OrderStatusEnum.PENDING.getStatus(), OrderStatusEnum.TRANSFER_FAIL.getStatus(), "微信支付");

                    }
                });*/
            }
        });

        // 调用调配药师签署接口
        // List<OrderItem> items = OrderServiceImpl.this.orderItemRepository.findAllByOrderId(orderId);
        // this.pharmacistFeignClient.allocateAudit(items.get(0).getRecomId());
    }

    private PrescriptionPlatformResponseVO synchroPrescriptionPlatformRecom(Long orderId) {
        PrescriptionPlatformResponseVO result = null;
        try {
            List<PlatformRecomProducVO> drugList = new ArrayList<>();
            List<OrderItem> orderItems = orderItemRepository.findAllByOrderId(orderId);
            Map<Long, OrderItem> itemMap = orderItems.stream().collect(Collectors.toMap(OrderItem::getSkuId, e -> e));
            Set<Long> recomIds = orderItems.stream().map(e -> e.getRecomId()).collect(Collectors.toSet());
            List<MedicationRecomDetailBO> medicationRecomDetailBOS = recommendFeignClient.getRecomDetailList(StringUtil.convertCollection(recomIds));
            Map<Long, MedicationRecomDetailBO> medicationRecomDetails = medicationRecomDetailBOS.stream()
                .collect(Collectors.toMap(MedicationRecomDetailBO::getSkuId, e -> e));
            Set<Long> skuIds = orderItems.stream().map(e -> e.getSkuId()).collect(Collectors.toSet());
            Set<Long> productIds = orderItems.stream().map(e -> e.getProductId()).collect(Collectors.toSet());
            List<Sku> skus = skuRepository.findAllByIdIn(skuIds);
            Map<Long, Sku> skuMap = skus.stream().collect(Collectors.toMap(Sku::getId, e -> e));
            List<Product> products = productRepository.findAllByIdIn(productIds);
            Map<Long, Product> productMap = products.stream().collect(Collectors.toMap(Product::getId, e -> e));
            List<Medicine> medicines = medicineRepository.findAllByProductIdIn(productIds);
            Map<Long, Medicine> medicineMap = medicines.stream().collect(Collectors.toMap(Medicine::getProductId, e -> e));
            // 药品信息
            List<PlatformRecomProducVO> platformRecomProducVOS = new ArrayList<>();
            for (Sku sku : skus) {
                Long skuId = sku.getId();
                OrderItem orderItem = itemMap.get(skuId);
                MedicationRecomDetailBO medicationRecomDetail = medicationRecomDetails.get(skuId);
                Medicine medicine = medicineMap.get(sku.getProductId());
                Product product = productMap.get(sku.getProductId());
                platformRecomProducVOS.add(PlatformRecomProducVO.builder()
                    .drugCode(sku.getNumber()).drugFee(orderItem.getSalePrice()).drugName(sku.getName())
                    .drugTotalFee(orderItem.getSalePrice() * orderItem.getQuantity())
                    .specification(sku.getPackingSpec()).total(orderItem.getQuantity().toString())
                    .useDose(medicationRecomDetail.getEachDosageCount() + " " + medicationRecomDetail.getEachDoseUnit())
                    .usePathways(null).usingDays(medicationRecomDetail.getDrugCycleDays().toString())
                    .licenseNumber(medicine.getApprovalNumber()).standardCode(medicine.getStandardCode())
                    .producer(product.getProductionEnterprise()).build());
            }
            for (Long recomId : recomIds) {
                // 处方信息
                PrescriptionPlatformRecomInfoBO platformRecomInfo = recommendFeignClient.getPlatformRecomInfo(recomId);
                Order order = orderRepository.get(orderId);
                OrderUser orderUser = orderUserRepository.findByOrderIdAndType(orderId, OrderUser.ORDER_TYPE_SHOPPING);
                // 处方金额
                Integer totalAmount = orderItems.stream().collect(Collectors.summingInt(e -> e.getQuantity() * e.getSalePrice()));
                PlatformRecomVO platformRecomVO = PlatformRecomVO.builder()
                    .sourceId(ConfigUtil.getString("prescription.platform.sourceId")).pharmacyCode(null)
                    .actualFee(order.getRealPay())
                    .address(orderUser.getProvice() + " " + orderUser.getCity() + " " + orderUser.getCountry() + " "
                        + orderUser.getAddress())
                    .certificateType("1").couponFee("0").distributionFlag("1").giveMode("1").medicalPay("0")
                    .orderTotalFee(order.getTotalAmount())
                    .patientAddress(orderUser.getProvice() + " " + orderUser.getCity() + " " + orderUser.getCountry()
                        + " " + orderUser.getAddress())
                    .payMode("1").recipeFee(totalAmount).recipeType("1").recipientName(orderUser.getReceiver())
                    .recipientTel(orderUser.getPhone()).audiDate(platformRecomInfo.getPharmacistSignTime())
                    .auditor(platformRecomInfo.getPharmacistName()).certificate(platformRecomInfo.getPatientIdCard())
                    .createDate(platformRecomInfo.getCreatedAt()).departId(platformRecomInfo.getDoctorDepartmentCode())
                    .departName(platformRecomInfo.getDoctorDepartmentName())
                    .doctorName(platformRecomInfo.getDoctorName())
                    .doctorNumber(platformRecomInfo.getDoctorId().toString()).image(platformRecomInfo.getPdfBase64())
                    .organDiseaseId(platformRecomInfo.getDiagnosisCode())
                    .organDiseaseName(platformRecomInfo.getDiagnosisName())
                    .patientName(platformRecomInfo.getPatientName())
                    .patientTelpatient(platformRecomInfo.getPatientPhone())
                    .recipeCode(platformRecomInfo.getSerialNumber()).recipeId(platformRecomInfo.getId().toString())
                    .drugList(platformRecomProducVOS).organCode(ConfigUtil.getString("ehp.hospital.code"))
                    .organName(ConfigUtil.getString("ehp.hospital.name")).build();
                log.info("===同步处方流转平台处方信息，参数为 platformRecomVO {}", platformRecomVO.toString());
                // 同步处方信息
//                result = b2cRemoter.synchroPrescriptionPlatformRecom(prescriptionPlatformService.getAccessToken(), platformRecomVO);
            }
        } catch (BusinessException e) {
            log.error("处方同步处方流转平台业务异常， 订单ID {}，异常信息 {}", orderId, e);
            // TODO 需要做补充再次同步机制
        } catch (Exception e) {
            log.error("处方同步处方流转平台系统异常， 订单ID {}，异常信息 {}", orderId, e);
            // TODO 需要做补充再次同步机制
        }
        return result;
    }

    @Override
    public ShippingInfoVO getShippingInfo(Long orderId, Long userId) {
        Order order = this.orderRepository.findByIdAndUserId(orderId, userId);
        List<DeliveryTraceInfoVO> traces = Lists.newArrayList();
        LogisticsOrder logisticsOrder = this.logisticsOrderRepository.findByOrderId(orderId);
        List<LogisticsRouteOrder> logisticsRouteOrderList = this.logisticsRouteOrderRepository.getByOrderIdOrderByCreatedAtDesc(orderId);
        logisticsRouteOrderList.forEach(logisticsRouteOrder -> {
            String shippingDateFormat = "yyyy/MM/dd HH:mm:ss";
            String createdTimeStr = DateUtil.formatDate(logisticsRouteOrder.getCreatedAt(), shippingDateFormat);
            traces.add(DeliveryTraceInfoVO.builder().opeRemark(logisticsRouteOrder.getContent()).opeTime(createdTimeStr).build());
        });
        log.info("获取订单物流信息, userId: {}, orderId: {}, traces: {}", userId, orderId, JsonMapper.toJson(traces));
        LogisticseCompany logisticseCompany = this.logisticseCompanyRepository.get(logisticsOrder.getLogisticsCompanyId());
        return ShippingInfoVO.builder()
            .number(logisticsOrder.getDeliveryId()).orderSn(order.getOrderSn())
            .orderStatusName(OrderStatusEnum.getFrontDeskStatus(order.getOrderStatus()).getFrontDeskStatusDescribe())
            .traces(traces).carrier(logisticseCompany.getName()).build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditOrder(Set<Long> orderIds, String operator) throws OrderBusinessException {
        List<Order> orders = this.orderRepository.findByIdIn(orderIds);
        for (Order order : orders) {
            Integer beforeOrderStatus = order.getOrderStatus();
            if (!beforeOrderStatus.equals(OrderStatusEnum.WAIT_AUDIT.getStatus())) {
                throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_ERROR);
            }
            order.setOrderStatus(OrderStatusEnum.AUDIT_COMPLETE.getStatus());
            order.setChangedAt(new Date());
            order.setChangedBy(operator);
            this.orderLogService.save(order.getId(), "人工/系统审核完成，等待库房分配。", beforeOrderStatus, OrderStatusEnum.AUDIT_COMPLETE.getStatus(), operator);
        }
        this.orderRepository.update(orders);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void returnAuditOrder(Set<Long> orderIds, String operator) {
        List<Order> orders = this.orderRepository.findByIdIn(orderIds);
        for (Order order : orders) {
            this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    Long orderId = order.getId();
                    try {
                        Integer beforeOrderStatus = order.getOrderStatus();
                        List<OrderItem> orderItemList = OrderServiceImpl.this.orderItemRepository.findAllByOrderId(orderId);
                        List<StockQuantityBO> pqs = orderItemList.stream()
                            .map(cartItem -> new StockQuantityBO(cartItem.getSkuId(), cartItem.getQuantity()))
                            .collect(Collectors.toList());
                        if (OrderStatusEnum.AUDIT_COMPLETE.getStatus().equals(beforeOrderStatus)) {
                            log.info("订单状态为审核完成，未操作库存，直接变更反审状态 orderId:{}", orderId);
                        } else if (OrderStatusEnum.ASSIGNED_COMPLETE.getStatus().equals(beforeOrderStatus)) {
                            log.info("订单状态为库房分配完成,减少预占，orderId:{}", orderId);
                            OrderServiceImpl.this.stockService.returnAudit(orderId, order.getOrderSn(), order.getWarehouseId(), 0, pqs, operator);
                        } else if (OrderStatusEnum.WAREHOUSE_CONFIRM.getStatus().equals(beforeOrderStatus)) {
                            log.info("订单状态为药店已确认订单,减少占用，orderId:{}", orderId);
                            OrderServiceImpl.this.stockService.returnAudit(orderId, order.getOrderSn(), order.getWarehouseId(), 1, pqs, operator);
                        } else {
                            log.warn("当前订单状态不正确，不能进行反审操作,orderId:{} orderStatus:{}", orderId, beforeOrderStatus);
                            return;
                        }
                        order.setOrderStatus(OrderStatusEnum.WAIT_AUDIT.getStatus());
                        order.setChangedAt(new Date());
                        order.setChangedBy(operator);
                        OrderServiceImpl.this.orderLogService
                            .save(orderId, "CMS反审核完成，等待人工/系统审核。", beforeOrderStatus,
                                OrderStatusEnum.WAIT_AUDIT.getStatus(), operator);
                        OrderServiceImpl.this.orderRepository.update(order);
                    } catch (Exception e) {
                        log.error("订单反审核失败 orderId:{}", orderId, e);
                        transactionStatus.setRollbackOnly();
                    }
                }
            });
        }
    }

    @Override
    public void warehouseConfirmOrder(Long warehouseId, Set<Long> orderIds, String operator, Integer status) throws OrderBusinessException {
        List<Order> orders = this.orderRepository.findByIdInAndWarehouseId(orderIds, warehouseId);
        for (Order order : orders) {
            Long orderId = order.getId();
            Integer orderStatus = order.getOrderStatus();
            if (!orderStatus.equals(OrderStatusEnum.ASSIGNED_COMPLETE.getStatus())) {
                throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_ERROR);
            }
            if (status == 0) {
                order.setOrderStatus(OrderStatusEnum.WAREHOUSE_CONFIRM.getStatus());
                this.orderLogService.save(orderId, "药店已确认订单，等待发货。", orderStatus, OrderStatusEnum.WAREHOUSE_CONFIRM.getStatus(), operator);
            } else {
                List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderId(orderId);
                List<StockQuantityBO> pqs = orderItemList.stream()
                    .map(cartItem -> new StockQuantityBO(cartItem.getSkuId(), cartItem.getQuantity()))
                    .collect(Collectors.toList());
                //清理占用
                this.stockService.warehouseReject(orderId, order.getOrderSn(), warehouseId, pqs);
                order.setWarehouseId(null);
                order.setOrderStatus(OrderStatusEnum.WAIT_AUDIT.getStatus());
                this.orderLogService.save(orderId, "药店已拒绝订单，等待重新审核。", orderStatus, OrderStatusEnum.WAIT_AUDIT.getStatus(), operator);
            }
            order.setChangedAt(new Date());
            order.setChangedBy(operator);
        }
        this.orderRepository.update(orders);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deliveryOrder(Long warehouseId, Set<OrderDeliveryBO> deliveryInfo,
        String operator) throws OrderBusinessException, StockServiceException {
        Set<Long> orderIds = deliveryInfo.stream().map(OrderDeliveryBO::getOrderId).collect(Collectors.toSet());
        Map<Long, OrderDeliveryBO> deliveryMap = deliveryInfo.stream().collect(Collectors.toMap(OrderDeliveryBO::getOrderId, Function.identity()));
        List<Order> orders = this.orderRepository.findByIdInAndWarehouseId(orderIds, warehouseId);

        // 查询快递公司
        Set<Long> ids = deliveryInfo.stream().map(OrderDeliveryBO::getExpressId).collect(Collectors.toSet());
        List<LogisticseCompany> logisticseCompanyList = logisticseCompanyRepository.findAllByIdIn(ids);
        Map<Long, String> longOrderDeliveryBOMap = logisticseCompanyList.stream()
            .collect(Collectors.toMap(LogisticseCompany::getId, LogisticseCompany::getName, (a, b) -> b));
        List<LogisticsOrder> logisticsOrders = Lists.newArrayList();
        for (Order order : orders) {
            Long orderId = order.getId();
            Integer orderStatus = order.getOrderStatus();
            if (!orderStatus.equals(OrderStatusEnum.WAREHOUSE_CONFIRM.getStatus())) {
                throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_ERROR);
            }
            List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderId(orderId);
            List<StockQuantityBO> pqs = orderItemList.stream()
                .map(cartItem -> new StockQuantityBO(cartItem.getSkuId(), cartItem.getQuantity()))
                .collect(Collectors.toList());
            this.stockService.deliveryOrder(orderId, order.getOrderSn(), order.getWarehouseId(), pqs);
            order.setPickupMan(operator);
            order.setSendTime(new Date());
            order.setOrderStatus(OrderStatusEnum.SHIPPING.getStatus());
            order.setChangedAt(new Date());
            order.setChangedBy(operator);
            OrderDeliveryBO delivery = deliveryMap.get(orderId);
            logisticsOrders.add(LogisticsOrder.builder()
                .orderId(orderId).orderSn(order.getOrderSn()).deliveryId(delivery.getDeliveryId())
                .logisticsCompanyId(delivery.getExpressId()).type(LogisticsOrder.TYPE_NORMAL_DISTRIBUTION_LIST)
                .status(LogisticsOrder.STATUS_NORMAL).createdAt(new Date()).createdBy(operator).changedAt(new Date())
                .changedBy(operator).build());
            this.orderLogService.save(orderId, "订单已发货。", orderStatus, OrderStatusEnum.SHIPPING.getStatus(), operator);
            // 发送
            List<String> productNames =
                orderItemList.stream().map(OrderItem::getProductName).collect(Collectors.toList());
            Map<String, Object> paramsMap = new HashMap<>(6);
            paramsMap.put("orderNumber", order.getOrderSn());
            paramsMap.put("expressDeliveryCompany", longOrderDeliveryBOMap.get(delivery.getExpressId()));
            paramsMap.put("expressDeliveryNumber", delivery.getDeliveryId());
            paramsMap.put("orderItem", String.join("、", productNames));
            patientFeignClient
                .publishMaMessage(MsgMaPublishParamBO.builder().doctorId(orderItemList.get(0).getDoctorId())
                    .patientId(order.getUserId()).path("/pages/orderDetail/orderDetail?orderId=" + order.getId())
                    .msgType(MsgMaPublishParamBO.MsgType.SHIP_NOTICE).content(JSONUtil.toJsonStr(paramsMap)).build());
        }
        this.orderRepository.update(orders);
        this.logisticsOrderRepository.saveAll(logisticsOrders);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrder(UpdateOrderBO updateOrder, String operator) throws BusinessException, StockServiceException {
        Long orderId = updateOrder.getOrderId();
        Order order = this.orderRepository.get(orderId);
        Integer orderStatus = order.getOrderStatus();
        if (!orderStatus.equals(OrderStatusEnum.PENDING.getStatus())) {
            throw new OrderBusinessException(OrderExceptionCodes.ORDER_STATUS_ERROR);
        }

        List<StockQuantityBO> stockQuantity = this.buildStockQuantity(orderId);

        Long warehouseId = order.getWarehouseId();
        Long modifyWarehouseId = updateOrder.getWarehouseId();
        //修改仓库
        this.stockService.modifyWarehouse(orderId, order.getOrderSn(), warehouseId, modifyWarehouseId, stockQuantity);
        order.setWarehouseId(modifyWarehouseId);
        order.setChangedAt(new Date());
        order.setChangedBy(operator);
        this.orderRepository.update(order);

        OrderUser orderUser = this.orderUserRepository.findByOrderIdAndType(orderId, OrderUser.ORDER_TYPE_SHOPPING);
        orderUser.setReceiver(updateOrder.getReceiver());
        CityBO city = this.cityService.getCityById(updateOrder.getCityId());
        orderUser.setProvice(city.getProvice());
        orderUser.setCity(city.getCity());
        orderUser.setCountry(city.getCountry());
        orderUser.setCityId(updateOrder.getCityId());
        orderUser.setAddress(updateOrder.getAddress());
        orderUser.setZipcode(updateOrder.getZipcode());
        orderUser.setPhone(updateOrder.getPhone());
        orderUser.setChangedAt(new Date());
        orderUser.setChangedBy(operator);
        this.orderUserRepository.update(orderUser);
        this.orderLogService.save(orderId, "修改订单信息。", orderStatus, orderStatus, operator);
    }

    private List<StockQuantityBO> buildStockQuantity(Long orderId) {
        List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderId(orderId);
        return orderItemList.stream()
            .map(cartItem -> new StockQuantityBO(cartItem.getSkuId(), cartItem.getQuantity()))
            .collect(Collectors.toList());
    }

    /**
     * 查询推荐所属订单是否未支付
     *
     * @param recomId
     * @return
     */
    @Override
    public boolean hasWaitPayByRecomId(Long recomId) {
        List<OrderItem> items = this.orderItemRepository.findAllByRecomId(recomId);
        if (items != null && items.size() > 0) {
            Optional<Order> order = this.orderRepository.findById(items.get(0).getOrderId());
            Integer orderStatus = order.map(Order::getOrderStatus).orElse(null);
            if (OrderStatusEnum.PENDING.getStatus().equals(orderStatus)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public OrderStatisticalVO getOrderStatisticalInfo(Long patientId) {
        OrderStatisticalVO result = OrderStatisticalVO.builder()
            .pendingNum(orderRepository.countByUserIdAndOrderStatus(patientId, OrderStatusEnum.PENDING.getStatus()))
            .transferSuccessNum(orderRepository.countByUserIdAndOrderStatusIn(patientId,
                ListUtil.toList(OrderStatusEnum.WAIT_AUDIT.getStatus(), OrderStatusEnum.AUDIT_COMPLETE.getStatus(),
                    OrderStatusEnum.ASSIGNED_COMPLETE.getStatus(), OrderStatusEnum.WAREHOUSE_CONFIRM.getStatus())))
            .shippingNum(orderRepository.countByUserIdAndOrderStatus(patientId, OrderStatusEnum.SHIPPING.getStatus()))
            .cancelledNum(orderRepository.countByUserIdAndOrderStatus(patientId, OrderStatusEnum.CANCELLED.getStatus()))
            .build();
        return result;
    }

    @Override
    public Map<Long, Long> getOrderByRecomIds(Set<Long> recomIds) {
        List<OrderItem> list = orderItemRepository.findAllByRecomIdIn(recomIds);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        return list.stream().collect(Collectors.toMap(OrderItem::getRecomId, OrderItem::getOrderId, (a, b) -> b));
    }

    @Override
    public CustomOrderInfoBO getOrderDetailByOrderSn(String orderSn) throws BusinessException {
        Order order = this.orderRepository.findByOrderSn(orderSn);
        if (order == null) {
            log.warn("订单不存在 orderSn:{}", orderSn);
            throw new BusinessException(OrderExceptionCodes.ORDER_NOT_FOUND);
        }

        List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderId(order.getId());
        OrderUser orderUser =
            this.orderUserRepository.findByOrderIdAndType(order.getId(), OrderUser.ORDER_TYPE_SHOPPING);

        int itemQuantity = orderItemList.stream().mapToInt(OrderItem::getQuantity).sum();

        String carrier = "";
        LogisticsOrder logisticsOrder = this.logisticsOrderRepository.findByOrderId(order.getId());
        if (logisticsOrder != null && null != logisticsOrder.getLogisticsCompanyId()) {
            LogisticseCompany logisticseCompany =
                this.logisticseCompanyRepository.get(logisticsOrder.getLogisticsCompanyId());
            carrier = logisticseCompany.getName();
        }

        OrderStatusEnum orderStatusEnum = OrderStatusEnum.getFrontDeskStatus(order.getOrderStatus());
        return CustomOrderInfoBO.builder().orderId(order.getId()).orderSn(order.getOrderSn()).itemQuantity(itemQuantity)
            .orderTime(order.getCreatedAt()).totalAmount(order.getTotalAmount()).freight(order.getFreight())
            .couponPay(order.getCouponPay()).realPay(order.getRealPay()).status(order.getOrderStatus())
            .statusName(orderStatusEnum.getFrontDeskStatusDescribe())
            .items(this.transferCustomOrderItemBO(orderItemList)).remark(order.getRemark())
            // 待付款状态提示剩余时间
            .tips(OrderStatusEnum.PENDING.equals(orderStatusEnum) ? ORDER_AUTO_CANCEL_TIPS : "")
            .receiverInfo(CustomOrderReceiverBO.builder().receiver(orderUser.getReceiver()).phone(orderUser.getPhone())
                .province(orderUser.getProvice()).city(orderUser.getCity()).county(orderUser.getCountry())
                .addr(orderUser.getAddress()).build())
            .payType("微信支付")
            // 查询物流
            .carrier(carrier).build();
    }
}